package com.handpay.nocardpay.baselibrary.crypto;

import android.text.TextUtils;

import com.handpay.nocardpay.baselibrary.config.NoCardPayConfig;
import com.handpay.nocardpay.baselibrary.logutils.HPLog;
import com.handpay.nocardpay.baselibrary.utils.ClientEngine;

import java.security.NoSuchAlgorithmException;
import java.util.Locale;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Created by fplei on 2017/8/9.
 * this class use to encrypt or decrypt data for local
 */

public class LocalEncrypt {
    private static LocalEncrypt localEncrypt = null;
    private static final String Contant = "NCARDPAY";
    private static final int define_len = 8;
    private String tempKey;
    private static final String Algorithm = "DESede"; // 3DESede
    private final static char[] mChars = "0123456789ABCDEF".toCharArray();
    private final static String mHexStr = "0123456789ABCDEF";

    private LocalEncrypt() {
        tempKey = createSeed();
        HPLog.i(HPLog.LFP_TAG, "tempKey=" + tempKey.toString());
    }

    //this key Consist of constant+IMEI+Contant
    private String createSeed() {
        StringBuffer sb = new StringBuffer();
        String temp0 = NoCardPayConfig.ContantKeyModulus;
        String temp1 = ClientEngine.getInstance().DEVICE_ID;
        //if this value is null,we will get UUID
        if (TextUtils.isEmpty(temp1)) {
            temp1 = ClientEngine.getInstance().getDeviceUuid();
        }
        if (temp1.length() < 8) {
            for (int i = 0; i < 8 - temp1.length(); i++) {
                if (i % 2 == 0) {
                    temp1 += "1";
                } else {
                    temp1 += "3";
                }
            }
        } else if (temp1.length() > 8) {
            temp1 = temp1.substring(0, 8);
        }
        char[] arry0 = temp0.toCharArray();
        char[] arry1 = temp1.toCharArray();
        char[] arry2 = Contant.toCharArray();
        //分散
        for (int i = 0; i < define_len; i++) {
            sb.append(arry0[i] + arry1[define_len - i - 1] + arry2[i]);
        }
        return sb.toString();
    }

    public static LocalEncrypt with() {
        if (localEncrypt == null) {
            synchronized (LocalEncrypt.class) {
                if (localEncrypt == null) {
                    localEncrypt = new LocalEncrypt();
                }
            }
        }
        return localEncrypt;
    }

    /**
     * 加密
     *
     * @param src
     * @return
     */
    public String encryptMode(String src) {
        try {
            // 生成密钥
            SecretKey deskey = new SecretKeySpec(tempKey.getBytes(), Algorithm);
            // 加密
            Cipher c1 = Cipher.getInstance(Algorithm);
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            return bytesToHexString(c1.doFinal(src.getBytes()));
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (Exception e3) {
            e3.printStackTrace();
        }
        return "";
    }

    /**
     * 解密
     *
     * @param src
     * @return
     */
    public String decryptMode(String src) {
        try {
            // 生成密钥
            SecretKey deskey = new SecretKeySpec(tempKey.getBytes(), Algorithm);
            // 解密
            Cipher c1 = Cipher.getInstance(Algorithm);
            c1.init(Cipher.DECRYPT_MODE, deskey);
            return hexStr2Str(bytesToHexString(c1.doFinal(hexStringToBytes(src))));
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (Exception e3) {
            e3.printStackTrace();
        }
        return "";
    }

    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * Convert hex string to byte[]
     *
     * @param hexString the hex string
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    /**
     * 字符串转换成十六进制字符串
     *
     * @param str String 待转换的ASCII字符串
     * @return String 每个Byte之间空格分隔，如: [61 6C 6B]
     */
    public static String str2HexStr(String str) {
        StringBuilder sb = new StringBuilder();
        byte[] bs = str.getBytes();

        for (int i = 0; i < bs.length; i++) {
            sb.append(mChars[(bs[i] & 0xFF) >> 4]);
            sb.append(mChars[bs[i] & 0x0F]);
            sb.append(' ');
        }
        return sb.toString().trim();
    }

    /**
     * 十六进制字符串转换成 ASCII字符串
     *
     * @param hexStr String Byte字符串
     * @return String 对应的字符串
     */
    public static String hexStr2Str(String hexStr) {
        hexStr = hexStr.toString().trim().replace(" ", "").toUpperCase(Locale.US);
        char[] hexs = hexStr.toCharArray();
        byte[] bytes = new byte[hexStr.length() / 2];
        int iTmp = 0x00;

        for (int i = 0; i < bytes.length; i++) {
            iTmp = mHexStr.indexOf(hexs[2 * i]) << 4;
            iTmp |= mHexStr.indexOf(hexs[2 * i + 1]);
            bytes[i] = (byte) (iTmp & 0xFF);
        }
        return new String(bytes);
    }
}
